package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import com.arpa.ntocc.common.common.cache.DictCache;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.wms.cache.WarehouseCache;
import com.arpa.wms.domain.dto.ExpenseDetailDTO;
import com.arpa.wms.domain.dto.ExpenseDetailSettlementDTO;
import com.arpa.wms.domain.dto.OperationCostSourceDataDTO;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.ExpenseDetailStatusEnum;
import com.arpa.wms.domain.enums.StatusEnum;
import com.arpa.wms.domain.enums.StorageFeeTypeEnum;
import com.arpa.wms.domain.vo.ContractSchedulingVO;
import com.arpa.wms.domain.vo.ExpenseDetailVO;
import com.arpa.wms.mapper.ExpenseDetailMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.Future;

import static com.arpa.wms.common.util.OrderIdGeneratorUtil.OrderTypeEnum.CK;
import static com.arpa.wms.common.util.OrderIdGeneratorUtil.OrderTypeEnum.RK;

/**
 * <p>
 * 仓库费用明细 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-11-17
 */
@Slf4j
@Service
public class ExpenseDetailServiceImpl extends ServiceImpl<ExpenseDetailMapper, ExpenseDetail> implements IExpenseDetailService {

    private static final String BILLING = "billing";
    private static final String SETTLED_BEGIN = "settledBegin";

    @Resource
    private WarehouseCache warehouseCache;

    @Resource
    private PartyCache partyCache;

    @Resource
    private DictCache dictCache;


    private final IContractService contractService;
    private final IGoodsUnitConversionService goodsUnitConversionService;
    private final IInventoryService inventoryService;
    private final IInboundItemService inboundItemService;

    public ExpenseDetailServiceImpl(IContractService contractService,
                                    IGoodsUnitConversionService goodsUnitConversionService,
                                    IInventoryService inventoryService,
                                    IInboundItemService inboundItemService) {
        this.contractService = contractService;
        this.goodsUnitConversionService = goodsUnitConversionService;
        this.inventoryService = inventoryService;
        this.inboundItemService = inboundItemService;
    }

    private final static String CAN_NOT_QUERY_EXPENSIVE_DETAIL = "找不到费用明细";
    /**
     * 修改费用明细金额
     *
     * @param code    费用明细标识
     * @param amount  费用金额
     * @param remarks 备注
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean updateAmount(final String code, final BigDecimal amount, final String remarks) {

        if (null == amount || amount.compareTo(BigDecimal.ZERO) < 0) {
            log.error("计费信息修改失败，费用金额必须大于或等于0。CODE【{}】", code);
            throw new ServiceException("费用金额必须大于或等于0");
        }

        ExpenseDetail one = super.getOne(new QueryWrapper<ExpenseDetail>().lambda().eq(ExpenseDetail::getCode, code).eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode()));
        if (null == one) {
            log.error("计费信息修改失败，找不到费用明细。CODE【{}】", code);
            throw new ServiceException(CAN_NOT_QUERY_EXPENSIVE_DETAIL);
        }
        if (!one.getStatus().equals(ExpenseDetailStatusEnum.UNSETTLED)) {
            log.error("计费信息修改失败，已进行结算，不能修改。CODE【{}】", code);
            throw new ServiceException("费用已结算，不能修改");
        }
        if (one.getWaitingSettledMoney().compareTo(BigDecimal.ZERO) > 0 || one.getSettledMoney().compareTo(BigDecimal.ZERO) > 0) {
            log.error("计费信息修改失败，已进行结算，不能修改。CODE【{}】", code);
            throw new ServiceException("费用结算中，不能修改");
        }
        return super.update(new UpdateWrapper<ExpenseDetail>().lambda()
                .eq(ExpenseDetail::getCode, code)
                .eq(ExpenseDetail::getStatus, ExpenseDetailStatusEnum.UNSETTLED)
                .eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode())
                .set(ExpenseDetail::getUnsettledMoney, amount)
                .set(ExpenseDetail::getAmountMoney, amount)
                .set(ExpenseDetail::getWaitingSettledMoney, BigDecimal.ZERO)
                .set(ExpenseDetail::getSettledMoney, BigDecimal.ZERO)
                .set(ExpenseDetail::getRemarks, remarks)
                .set(ExpenseDetail::getModifiedName, partyCache.translate(UserUtil.getCode()))
                .set(ExpenseDetail::getModifiedBy, UserUtil.getCode())
        );
    }

    @Override
    public boolean removeByCode(String code) {

        ExpenseDetail one = super.getOne(new QueryWrapper<ExpenseDetail>().lambda().eq(ExpenseDetail::getCode, code).eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode()));
        if (null == one) {
            log.error("计费信息删除失败，找不到费用明细。CODE【{}】", code);
            throw new ServiceException(CAN_NOT_QUERY_EXPENSIVE_DETAIL);
        }
        if (!one.getStatus().equals(ExpenseDetailStatusEnum.UNSETTLED)) {
            log.error("计费信息删除失败，已进行结算，不能删除。CODE【{}】", code);
            throw new ServiceException("已进行结算，不能删除");
        }
        if (one.getWaitingSettledMoney().compareTo(BigDecimal.ZERO) > 0 || one.getSettledMoney().compareTo(BigDecimal.ZERO) > 0) {
            log.error("计费信息修改失败，已进行结算，不能删除。CODE【{}】", code);
            throw new ServiceException("费用结算中，不能删除");
        }

        return super.remove(new QueryWrapper<ExpenseDetail>().lambda().eq(ExpenseDetail::getCode, code)
                .eq(ExpenseDetail::getGroupCode, UserUtil.getBranchCode())
                .eq(ExpenseDetail::getStatus, ExpenseDetailStatusEnum.UNSETTLED));
    }

    /**
     * 查询列表
     *
     * @param expenseDetailDTO
     * @return
     */
    @Override
    public List<ExpenseDetailVO> queryList(ExpenseDetailDTO expenseDetailDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(expenseDetailDTO.getSortField())) {
            expenseDetailDTO.setSortField(CommonUtil.camel2Underline(expenseDetailDTO.getSortField()));
        }
        List<ExpenseDetailVO> expenseDetailVOList = baseMapper.queryList(expenseDetailDTO);
        // 权限限定只会有一个仓库信息
        String warehouseName = warehouseCache.translate(expenseDetailDTO.getWarehouseCode());
        if (IterUtil.isNotEmpty(expenseDetailVOList)) {
            expenseDetailVOList.forEach(r ->r.setWarehouseName(warehouseName));
        }
        return expenseDetailVOList;
    }

    /**
     * 查询合计,包含总数
     *
     * @param expenseDetailDTO
     * @return
     */
    @Override
    public ExpenseDetailVO queryListSum(ExpenseDetailDTO expenseDetailDTO) {
        ExpenseDetailVO expenseDetailVO = baseMapper.queryListSum(expenseDetailDTO);
        return expenseDetailVO;
    }


    /**
     * 定时生成仓储费用记录
     * *  1、查询出所有有效合同
     * *  2、遍历合同，包仓合同判断当前是否位计费日期（当前日期是否处在，开始计费日期+计费周期上，）
     * *  3、零仓合同查询库存收货批号获取相应的入库单及入库时间，（当前日期是否处在，入库日期+计费周期上，）
     *
     * @param groupCode 结构code
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Async
    public void generate(String groupCode) {
        //获取所有有效合同
        List<Contract> contracts = contractService.queryEffective(groupCode);
        for (Contract c : contracts) {
            //计费周期
            int billingCycle = c.getBillingCycle();
            //失效日期
            LocalDate gmtIneffective = c.getGmtIneffective();
            LocalDate today = LocalDate.now();
            if (Objects.equals(c.getStorageFeeType(), StorageFeeTypeEnum.WHOLE)) {
                generateWholeExpenseDetail(groupCode, c, billingCycle, gmtIneffective, today);
            }
            if (Objects.equals(c.getStorageFeeType(), StorageFeeTypeEnum.SCATTERED)) {
                generateScatteredExpenseDetail(groupCode, c, billingCycle, gmtIneffective, today);
            }
        }
    }

    private void generateScatteredExpenseDetail(String groupCode, Contract c, int billingCycle,
                                                LocalDate gmtIneffective, LocalDate today) {
        //获取货主所有库存
        List<Map<String, Object>> inventories = inventoryService.listMaps(
                new LambdaQueryWrapper<Inventory>().select(
                        Inventory::getWarehouseCode,
                        Inventory::getGoodsCode,
                        Inventory::getGoodCode,
                        Inventory::getGoodsName,
                        Inventory::getReceiveLot)
                        .eq(Inventory::getGroupCode,UserUtil.getBranchCode())
                        .eq(Inventory::getShipmentCode, c.getShipmentCode())
                        .gt(Inventory::getQuantity, BigDecimal.ZERO)
                        .eq(Inventory::getTempInventory, 0)
                        .eq(Inventory::getWarehouseCode, c.getWarehouseCode())
                        .isNotNull(Inventory::getReceiveLot)
                        .groupBy(Inventory::getWarehouseCode, Inventory::getGoodsCode, Inventory::getReceiveLot)
        );
        if (IterUtil.isNotEmpty(inventories)) {
            //货主当前有上架库存
            //遍历库存
            for (Map<String, Object> r : inventories) {
                //通过库存获取入库单号及入库时间
                Map<String, Object> item = inboundItemService.getItemInfo(
                        MapUtil.getStr(r, "warehouse_code"),
                        MapUtil.getStr(r, "goods_code"),
                        MapUtil.getStr(r, "receive_lot"));
                if (MapUtil.isNotEmpty(item)) {

                    String inboundCode = MapUtil.getStr(item, "inboundCode");
                    String receiveLot = MapUtil.getStr(item, "receiveLot");

                    //零仓合同中途修改后，开始计费日期为当前日期
                    //用入库日期
                    Date gmtCreated = MapUtil.getDate(item, "gmtCreated");
                    Date billingBegin = DateUtil.date(gmtCreated);
                    Date ineffective = java.sql.Date.valueOf(gmtIneffective);


                    //计算排期
                    List<Map<String, String>> date = ContractServiceImpl.generateRange(billingBegin, ineffective, billingCycle);

                    Optional<Map<String, String>> billing = date.stream().filter(map -> today.toString().equals(MapUtil.getStr(map, BILLING))).findFirst();

                    //零仓合同判断当前是否为计费日期（当前日期是否处在，订单日期+计费周期上）
                    billing.ifPresent(m -> {
                        //判断是否已生成
                        boolean exists = scatteredExpenseDetailExists(c.getCode(),
                                inboundCode,
                                receiveLot,
                                MapUtil.getStr(m, BILLING));
                        if (exists) {
                            //生成零仓费用
                            ContractSchedulingVO contractSchedulingVO = new ContractSchedulingVO();
                            BeanUtil.copyProperties(c, contractSchedulingVO);
                            contractSchedulingVO.setGroupCode(groupCode);
                            contractSchedulingVO.setContractCode(c.getCode());
                            contractSchedulingVO.setGmtSettledBegin(LocalDate.parse(MapUtil.getStr(m, SETTLED_BEGIN)));
                            contractSchedulingVO.setInboundCode(inboundCode);
                            ZoneId zoneId = ZoneId.systemDefault();
                            contractSchedulingVO.setGmtInboundCreated(gmtCreated.toInstant().atZone(zoneId).toLocalDate());
                            contractSchedulingVO.setGmtBilling(today);
                            contractSchedulingVO.setGoodsCode(MapUtil.getStr(r, "goods_code"));
                            contractSchedulingVO.setGoodCode(MapUtil.getStr(r, "good_code"));
                            contractSchedulingVO.setGoodsName(MapUtil.getStr(r, "goods_name"));
                            contractSchedulingVO.setReceiveLot(receiveLot);

                            generateScatteredExpenseDetail(contractSchedulingVO);
                        }
                    });
                }
            }
        }
    }

    private void generateWholeExpenseDetail(String groupCode, Contract c, int billingCycle,
                                            LocalDate gmtIneffective, LocalDate today) {
        //开始计费日期
        LocalDate gmtBillingBegin = c.getGmtBillingBegin();
        Date billingBegin = java.sql.Date.valueOf(gmtBillingBegin);
        Date ineffective = java.sql.Date.valueOf(gmtIneffective);
        //计费排期
        List<Map<String, String>> date = ContractServiceImpl.generateRange(billingBegin, ineffective, billingCycle);
        Optional<Map<String, String>> billing = date.stream()
                .filter(map -> today.toString().equals(MapUtil.getStr(map, BILLING)))
                .findFirst();
        //包仓合同判断当前是否位计费日期（当前日期是否处在，开始计费日期+计费周期上）
        billing.ifPresent(m -> {
            //判断是否已生成
            boolean exists = wholeExpenseDetailExists(c.getCode(), MapUtil.getStr(m, BILLING));
            if (exists) {
                ContractSchedulingVO r = new ContractSchedulingVO();
                BeanUtil.copyProperties(c, r);
                r.setGroupCode(groupCode);
                r.setContractCode(c.getCode());
                r.setGmtSettledBegin(LocalDate.parse(MapUtil.getStr(m, SETTLED_BEGIN)));
                r.setGmtBilling(today);
                //生成包仓仓储费
                generateWholeExpenseDetail(r);
            }
        });
    }


    /**
     * 判断计费日期是否已计费
     *
     * @param contractCode 合同code
     * @param billingDate  计费日期
     * @return true:没有计费，false：已计费
     */
    private boolean wholeExpenseDetailExists(String contractCode, String billingDate) {

        List<ExpenseDetail> expenseDetails = list(new LambdaQueryWrapper<ExpenseDetail>()
                .eq(ExpenseDetail::getContractCode, contractCode)
                .eq(ExpenseDetail::getFeeType, "仓储费")
                .eq(ExpenseDetail::getGmtSettledEnd, billingDate));
        if (IterUtil.isNotEmpty(expenseDetails)) {
            return false;
        } else {
            return true;
        }

    }

    /**
     * 判断计费日期是否已计费
     *
     * @param contractCode 合同code
     * @param inboundCode  入库单code
     * @param receiveLot   收货批号
     * @param billingDate  计费日期
     * @return true:没有计费，false：已计费
     */
    private boolean scatteredExpenseDetailExists(String contractCode, String inboundCode,
                                                 String receiveLot, String billingDate) {

        List<ExpenseDetail> expenseDetails = list(new LambdaQueryWrapper<ExpenseDetail>()
                .eq(ExpenseDetail::getContractCode, contractCode)
                .eq(ExpenseDetail::getFeeType, "仓储费")
                .eq(ExpenseDetail::getSourceCode, inboundCode)
                .eq(ExpenseDetail::getReceiveLot, receiveLot)
                .eq(ExpenseDetail::getGmtSettledEnd, billingDate));
        if (IterUtil.isNotEmpty(expenseDetails)) {
            return false;
        } else {
            return true;
        }

    }


    /**
     * 根据计费排期生成计费
     *
     * @param scheduling {@link ContractSchedulingVO}
     */
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void generateScatteredExpenseDetail(final ContractSchedulingVO scheduling) {
        String groupCode = scheduling.getGroupCode();
        BigDecimal quantity = baseMapper.getBillingQuantityForPiece(scheduling.getWarehouseCode(), scheduling.getShipmentCode(), scheduling.getGoodsCode(), scheduling.getReceiveLot());

        //计费数量
        if (null == quantity || quantity.compareTo(BigDecimal.ZERO) == 0) {
            //库存为0不及费用
            return;
        }
        BigDecimal weight = BigDecimal.ZERO;
        BigDecimal location ;
        BigDecimal amountMoney;
        //查询重量
        GoodsUnitConversion goodsUnitConversion = goodsUnitConversionService.getOne(new QueryWrapper<GoodsUnitConversion>()
                .lambda().select(GoodsUnitConversion::getWeight)
                .eq(GoodsUnitConversion::getGoodsCode, scheduling.getGoodsCode())
                .eq(GoodsUnitConversion::getFlag, 1));
        if (null != goodsUnitConversion && goodsUnitConversion.getWeight().compareTo(BigDecimal.ZERO) > 0) {
            weight = quantity.multiply(goodsUnitConversion.getWeight()).divide(BigDecimal.valueOf(1000));
        }


        switch (scheduling.getBillingUnit()) {
            //数量小于最小计费量以最小计费量为准
            case PIECE: {
                //按件收费
                amountMoney = scheduling.getUnitPrice().multiply(quantity.compareTo(scheduling.getMinBillingVolume()) < 0 ? scheduling.getMinBillingVolume() : quantity);
                break;
            }
            case TON: {

                //计费数量
                if (null == weight || weight.compareTo(BigDecimal.ZERO) == 0) {
                    //库存为0不及费用
                    return;
                }
                //千克转换成吨
                amountMoney = scheduling.getUnitPrice().multiply(weight.compareTo(scheduling.getMinBillingVolume()) < 0 ? scheduling.getMinBillingVolume() : weight);
                break;
            }
            default: {
                //按所占库位
                location = baseMapper.getBillingQuantityForLocation(scheduling.getWarehouseCode(), scheduling.getShipmentCode(), scheduling.getGoodsCode(), scheduling.getReceiveLot());
                amountMoney = scheduling.getUnitPrice().multiply(location.compareTo(scheduling.getMinBillingVolume()) < 0 ? scheduling.getMinBillingVolume() : location);
                break;
            }
        }
        if (null == amountMoney || amountMoney.compareTo(BigDecimal.ZERO) == 0) {
            //库存为0不及费用
            return;
        }


        ExpenseDetail expenseDetail = new ExpenseDetail();

        expenseDetail.setCode(IdUtil.simpleUUID());
        expenseDetail.setGroupCode(scheduling.getGroupCode());
        expenseDetail.setSourceCode(scheduling.getInboundCode());
        expenseDetail.setGmtOrder(scheduling.getGmtInboundCreated());
        expenseDetail.setContractCode(scheduling.getContractCode());
        expenseDetail.setWarehouseCode(scheduling.getWarehouseCode());
        expenseDetail.setWarehouseName(scheduling.getWarehouseName());
        expenseDetail.setShipmentCode(scheduling.getShipmentCode());
        expenseDetail.setShipmentName(scheduling.getShipmentName());
        expenseDetail.setGoodsCode(scheduling.getGoodsCode());
        expenseDetail.setGoodsName(scheduling.getGoodsName());
        expenseDetail.setGoodCode(scheduling.getGoodCode());
        expenseDetail.setQuantity(quantity);
        expenseDetail.setWeight(weight);
        expenseDetail.setFeeType("仓储费");
        expenseDetail.setReceiveLot(scheduling.getReceiveLot());
        expenseDetail.setBillingUnit(scheduling.getBillingUnit());
        expenseDetail.setUnitPrice(scheduling.getUnitPrice());
        //计算费用
        expenseDetail.setAmountMoney(amountMoney);
        expenseDetail.setGmtSettledBegin(scheduling.getGmtSettledBegin());
        expenseDetail.setGmtSettledEnd(scheduling.getGmtBilling());
        expenseDetail.setStatus(ExpenseDetailStatusEnum.UNSETTLED);
        expenseDetail.setUnsettledMoney(expenseDetail.getAmountMoney());
        save(expenseDetail);
        List<String> contractSchedulingCodes = new ArrayList<>();
        contractSchedulingCodes.add(scheduling.getCode());
        //更新计费排期的计费状态
        baseMapper.updateBillingSchedulingStatus(contractSchedulingCodes,groupCode);
    }

    /**
     * 生成包仓计费明细
     *
     * @param scheduling {@link ContractSchedulingVO}
     */
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void generateWholeExpenseDetail(final ContractSchedulingVO scheduling) {
        String groupCode = scheduling.getGroupCode();
        ExpenseDetail expenseDetail = new ExpenseDetail();
        BeanUtil.copyProperties(scheduling, expenseDetail, "status");
        expenseDetail.setCode(IdUtil.simpleUUID());
        expenseDetail.setGroupCode(groupCode);
        expenseDetail.setSourceCode(scheduling.getInboundCode());
        expenseDetail.setGmtOrder(scheduling.getGmtInboundCreated());
        expenseDetail.setFeeType("仓储费");
        expenseDetail.setGmtSettledBegin(scheduling.getGmtSettledBegin());
        expenseDetail.setGmtSettledEnd(scheduling.getGmtBilling());
        expenseDetail.setStatus(ExpenseDetailStatusEnum.UNSETTLED);
        //包仓金额：单价*数量
        expenseDetail.setAmountMoney(scheduling.getUnitPrice().multiply(scheduling.getQuantity()));
        expenseDetail.setUnsettledMoney(expenseDetail.getAmountMoney());
        super.save(expenseDetail);
        List<String> contractSchedulingCodes = new ArrayList<>();
        contractSchedulingCodes.add(scheduling.getCode());
        //更新计费排期的计费状态
        baseMapper.updateBillingSchedulingStatus(contractSchedulingCodes,groupCode);
    }


    /**
     * 生成作业费
     *
     * @param sourceDataDTO 货主标识
     */
    @Override
    public void generateOperationCost(OperationCostSourceDataDTO sourceDataDTO) {

        //获取仓储合同
        Contract entity = contractService.getOne(new QueryWrapper<Contract>().lambda()
                .eq(Contract::getGroupCode,UserUtil.getBranchCode())
                .eq(Contract::getShipmentCode, sourceDataDTO.getShipmentCode())
                .eq(Contract::getWarehouseCode, sourceDataDTO.getWarehouseCode())
                .eq(Contract::getStatus, StatusEnum.ACTIVE)
                .le(Contract::getGmtEffective, sourceDataDTO.getGmtOrderCreated())
                .ge(Contract::getGmtIneffective, sourceDataDTO.getGmtOrderCreated())
        );
        if (entity != null) {
            //仓储合同作业费明细
            List<ContractOperationCost> contractOperationCosts = contractService.getContractOperationCosts(entity.getCode());
            List<ExpenseDetail> list = new ArrayList<>();
            contractOperationCosts.stream().filter(e -> null != e.getBillingUnit() && e.getUnitPrice().compareTo(BigDecimal.ZERO) > 0).forEach(r -> {
                        if (r.getInboundUse() == 1 && sourceDataDTO.getType().equals(RK)) {
                            list.add(makeExpenseDetail(sourceDataDTO, entity, r));
                        }
                        if (r.getOutboundUse() == 1 && sourceDataDTO.getType().equals(CK)) {
                            list.add(makeExpenseDetail(sourceDataDTO, entity, r));
                        }
                    }
            );
            if (IterUtil.isNotEmpty(list)) {
                saveBatch(list);
            }
        }
    }


    /**
     * 构建ExpenseDetail
     *
     * @param sourceDataDTO
     * @param entity
     * @param r
     * @return
     */
    private ExpenseDetail makeExpenseDetail(OperationCostSourceDataDTO sourceDataDTO, Contract entity, ContractOperationCost r) {
        ExpenseDetail expenseDetail = new ExpenseDetail();
        BeanUtil.copyProperties(sourceDataDTO, expenseDetail, false);
        expenseDetail.setCode(IdUtil.simpleUUID());
        expenseDetail.setWeight(sourceDataDTO.getWeight().divide(BigDecimal.valueOf(1000)));
        expenseDetail.setGroupCode(entity.getGroupCode());
        expenseDetail.setContractCode(entity.getCode());
        expenseDetail.setSourceCode(sourceDataDTO.getSourceCode());
        expenseDetail.setGmtOrder(sourceDataDTO.getGmtOrderCreated());
        expenseDetail.setFeeType(dictCache.translate("wmsFeeType", r.getFeeType(),UserUtil.getBranchCode()));
        expenseDetail.setUnitPrice(r.getUnitPrice());
        expenseDetail.setGmtSettledBegin(sourceDataDTO.getGmtOrderCreated());
        expenseDetail.setGmtSettledEnd(sourceDataDTO.getGmtOrderCreated());
        expenseDetail.setDeleted(0);
        expenseDetail.setStatus(ExpenseDetailStatusEnum.UNSETTLED);
        expenseDetail.setBillingUnit(r.getBillingUnit());
        expenseDetail.setCreatedBy(UserUtil.getCode());
        expenseDetail.setCreatedName(partyCache.translate(UserUtil.getCode()));
        expenseDetail.setModifiedBy(UserUtil.getCode());
        expenseDetail.setModifiedName(partyCache.translate(UserUtil.getCode()));
        switch (r.getBillingUnit()) {
            //数量小于最小计费量以最小计费量为准
            case PIECE: {
                //金额
                expenseDetail.setAmountMoney(r.getUnitPrice().multiply(sourceDataDTO.getQuantity().compareTo(entity.getMinBillingVolume()) < 0 ? entity.getMinBillingVolume() : sourceDataDTO.getQuantity()));
                break;
            }
            case TON: {

                //金额
                expenseDetail.setAmountMoney(r.getUnitPrice().multiply((sourceDataDTO.getWeight().compareTo(entity.getMinBillingVolume()) < 0 ? entity.getMinBillingVolume() : sourceDataDTO.getWeight())).divide(BigDecimal.valueOf(1000)));
                break;
            }
            default: {
                //金额
                expenseDetail.setAmountMoney(r.getUnitPrice().multiply(sourceDataDTO.getLocationCount().compareTo(entity.getMinBillingVolume()) < 0 ? entity.getMinBillingVolume() : sourceDataDTO.getLocationCount()));
                break;
            }

        }
        expenseDetail.setUnsettledMoney(expenseDetail.getAmountMoney());
        return expenseDetail;
    }


    @Async
    @Override
    public Future check(final ExpenseDetailSettlementDTO.SettlementItem settlementItem, String groupCode) {
        final ExpenseDetail one = super.getOne(new QueryWrapper<ExpenseDetail>().lambda()
                .eq(ExpenseDetail::getCode, settlementItem.getCode()).eq(ExpenseDetail::getGroupCode, groupCode)
        );
        if (null == one) {
            log.error("找不到费用明细。CODE【{}】", settlementItem.getCode());
            throw new ServiceException(CAN_NOT_QUERY_EXPENSIVE_DETAIL);
        }
        if (one.getStatus().equals(ExpenseDetailStatusEnum.SETTLED)) {
            log.error("已进行结算，不能重复结算。CODE【{}】", settlementItem.getCode());
            throw new ServiceException("已进行结算，不能重复结算");
        }
        if (settlementItem.getThisSettledMoney().compareTo(BigDecimal.ZERO) < 1) {
            log.error("结算金额【{}】必须大于0", settlementItem.getThisSettledMoney());
            throw new ServiceException("结算金额【" + settlementItem.getThisSettledMoney() + "】必须大于0");
        }
        BigDecimal temp = one.getAmountMoney().subtract(one.getSettledMoney()).subtract(one.getWaitingSettledMoney());
        if (settlementItem.getThisSettledMoney().compareTo(temp) > 0) {
            log.error("本次结算金额【{}】不能大于未结算金额【{}】", settlementItem.getThisSettledMoney(), temp);
            throw new ServiceException("本次结算金额【" + settlementItem.getThisSettledMoney() + "】不能大于未结算金额【" + temp + "】");
        }
        return new AsyncResult(true);
    }

}
